<?php
/**
 * Zhub - A PHP Framework For API Developer
 *
 * This file is part of the Zhub package.
 *
 * (c) YiLong Fan <fantiq@163.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author fanyilong <fantiq@163.com>
 * @copyright 2017 fanyilong. All rights reserved.
 * @package Zhub
 * @date 18-2-11
 */

namespace Zhub\DataStruct;


use Zhub\DataStruct\Exceptions\MapReadOnly;
use Zhub\DataStruct\Exceptions\MapWriteOnly;
use Closure;

abstract class Map
{
    const READ_WRITE = 0b11;

    const READ_ONLY = 0b01;

    const WRITE_ONLY = 0b10;

    private $bucket = [];

    private $count = 0;

    private $state = self::READ_WRITE;

    public function __construct(array $init = [], int $status = self::READ_WRITE)
    {
        $this->bucket = $init;
        $this->count = count($this->bucket);
        $this->state = $status;
    }

    abstract public function get(string $key);
    abstract public function put(string $key);

    protected function getBucket(string $key, $def = null)
    {
        if (!$this->readable()) {
            throw new MapWriteOnly(static::class);
        }
        return $this->bucket[$key] ?? $def;
    }
    protected function putBucket(string $key, $value)
    {
        if (!$this->writable()) {
            throw new MapReadOnly(static::class);
        }
        if (!$this->has($key)) {
            ++$this->count;
        }

        $this->bucket[$key] = $value;
    }
    public function remove(string $key):bool
    {
        if ($this->has($key)) {
            unset($this->bucket[$key]);
            --$this->count;
            return true;
        }
        return false;
    }

    public function has(string $key):bool
    {
        return array_key_exists($key, $this->bucket);
    }

    public function isEmpty():bool
    {
        return $this->count === 0;
    }

    public function clear()
    {
        $this->bucket = [];
        $this->count = 0;
    }

    public function size():int
    {
        return $this->count;
    }

    public function readable():bool
    {
        return boolval($this->state & 0b01);
    }

    public function writable():bool
    {
        return boolval($this->state & 0b10);
    }

    public function toArray():array
    {
        return $this->bucket;
    }

    public function each(Closure $closure):array
    {
        $result = [];
        foreach ($this->bucket as $key => $val) {
            $result[] = call_user_func($closure, $key, $val);
        }
        return $result;
    }
    // TODO 这里提供方法给外部扩展 如果修改的是核心变量 bucket 这样的 就很难保证调用者会将这个值改成什么类型了
    public function accept(Closure $closure)
    {
    }

    public function toString():string
    {
        return var_export($this->bucket, true);
    }
    public function __toString():string
    {
        return $this->toString();
    }
}